package com.jiangnan.utils;

import com.jiangnan.config.AddressMap;
import com.jiangnan.enums.Protocol;
import com.jiangnan.model.*;
import com.jiangnan.queue.PacketQueue;
import jpcap.packet.*;

/**
 * 数据包常用工具
 *
 * @author chenliang
 * @version 1.0
 * @email wschenliang@aliyun.com
 */
public class PacketUtil {

    private static final AddressMap addressMap = AddressMap.newInstance();

    //将json转化为packet对象
    public static Packet convertString2Packet(String jsonString, String proto) {
        Packet packet = null;
        if ("TCP".equals(proto)) {
            packet = FastjsonUtils.json2Object(jsonString, TCPPacket.class);
        } else if ("UDP".equals(proto)) {
            packet = FastjsonUtils.json2Object(jsonString, UDPPacket.class);
        } else if ("ARP".equals(proto)) {
            packet = FastjsonUtils.json2Object(jsonString, ARPPacket.class);
        } else if ("ICMP".equals(proto)) {
            packet = FastjsonUtils.json2Object(jsonString, ICMPPacket.class);
        } else {
            packet = FastjsonUtils.json2Object(jsonString, Packet.class);
        }
        return packet;
    }

    public static PacketModel convertPacket2Model(Packet p) {
        if (p instanceof TCPPacket) {
            return convertTcpPacket2Model((TCPPacket) p);
        } else if (p instanceof UDPPacket) {
            return convertUdpPacket2Model((UDPPacket) p);
        } else if (p instanceof ICMPPacket) {
            return convertIcmpPacket2Model((ICMPPacket) p);
        } else if (p instanceof ARPPacket) {
            return convertArpPacket2Model((ARPPacket) p);
        }
        return null;
    }

    private static PacketModel convertArpPacket2Model(ARPPacket p) {
        ArpModel model = new ArpModel();
        model.setTime(p.sec);
        model.setCaplen(p.caplen);
        model.setPayload(StringUtils.toString(p.getHexData(), " "));
        model.setProto(PacketModel.PROTO_ARP);
        model.setPayload_len(p.getHexData().length);
        //eth
        EthernetPacket ethernetPacket = (EthernetPacket) p.datalink;
        String srcMac = HexUtil.byteArrayToHexString(ethernetPacket.src_mac, ".");
        if (srcMac.startsWith("/")) {
            srcMac = srcMac.substring(1);
        }
        model.setSrc_mac(srcMac);
        String dstMac = HexUtil.byteArrayToHexString(ethernetPacket.dst_mac, ".");
        if (dstMac.startsWith("/")) {
            dstMac = dstMac.substring(1);
        }
        model.setDst_mac(dstMac);
        model.setSrc_area(addressMap.getAddressByMac(srcMac));
        model.setDst_area(addressMap.getAddressByMac(dstMac));
        model.setEthType(ethernetPacket.frametype);
        return model;
    }

    private static PacketModel convertIcmpPacket2Model(ICMPPacket p) {
        IcmpModel model = new IcmpModel();
        packet2Model(p, model);
        model.setProto(PacketModel.PROTO_ICMP);
        return model;
    }

    private static void packet2Model(IPPacket p, IpModel model) {
        model.setTime(p.sec);
        model.setCaplen(p.caplen);
        model.setPayload(StringUtils.toString(p.getHexData(), " "));
        String srcIp = p.src_ip.toString();
        if (srcIp.startsWith("/")) {
            srcIp = srcIp.substring(1);
        }
        model.setSrc_ip(srcIp);
        String dstIp = p.dst_ip.toString();
        if (dstIp.startsWith("/")) {
            dstIp = dstIp.substring(1);
        }
        model.setDst_ip(dstIp);
        model.setPayload_len(p.getHexData().length);
        //eth
        EthernetPacket ethernetPacket = (EthernetPacket) p.datalink;
        String srcMac = HexUtil.byteArrayToHexString(ethernetPacket.src_mac, ".");
        if (srcMac.startsWith("/")) {
            srcMac = srcMac.substring(1);
        }
        model.setSrc_mac(srcMac);
        String dstMac = HexUtil.byteArrayToHexString(ethernetPacket.dst_mac, ".");
        if (dstMac.startsWith("/")) {
            dstMac = dstMac.substring(1);
        }
        model.setDst_mac(dstMac);
        model.setSrc_area(addressMap.getAddressByMac(srcMac));
        model.setDst_area(addressMap.getAddressByMac(dstMac));
        model.setEthType(ethernetPacket.frametype);
        //ip
        model.setV(p.version);
        model.setLen(p.length);
        model.setId(p.ident);
        model.setFlags_rb(p.r_flag);
        model.setFlags_df(p.d_flag);
        model.setFlags_mf(p.more_frag);
        model.setFlags_offset(p.offset);
        model.setTtl(p.hop_limit);
        model.setProtocol(p.protocol);
    }

    private static PacketModel convertUdpPacket2Model(UDPPacket p) {
        UdpModel model = new UdpModel();
        packet2Model(p, model);
        //udp
        model.setProto(PacketModel.PROTO_UDP);
        model.setSrc_port(p.src_port);
        model.setDst_port(p.dst_port);
        model.setUdp_len(p.length);
        return model;
    }

    private static PacketModel convertTcpPacket2Model(TCPPacket p) {
        TcpModel model = new TcpModel();
        packet2Model(p, model);
        //tcp
        model.setProto(PacketModel.PROTO_TCP);
        model.setSrc_port(p.src_port);
        model.setDst_port(p.dst_port);
        model.setSeq(p.sequence);
        model.setAck(p.ack_num);
        model.setFlags_urg(p.urg);
        model.setFlags_ack(p.ack);
        model.setFlags_push(p.psh);
        model.setFlags_reset(p.rst);
        model.setFlags_syn(p.syn);
        model.setFlags_fin(p.fin);
        model.setUrgent_pointer(p.urgent_pointer);
        model.setWindows_size_value(p.window);
        return model;
    }

    private static int NUM = 1; // 行编号
    private static long firstPacketUSec = 0;
    private static long firstPacketSec = 0;

    public static TableData convertPacket2TableData(Packet p) {
        TableData tableData = new TableData();
        String jsonInfo = FastjsonUtils.toJSONString(p);
        tableData.setJsonInfo(jsonInfo);
        //放入model进行展示用的
        PacketModel model = convertPacket2Model(p);
        if (model != null) {
            tableData.setPacketModel(FastjsonUtils.toJSONString(model));
        }
        if (p instanceof TCPPacket) {
            tcpPacketHandle((TCPPacket) p, tableData);
        } else if (p instanceof UDPPacket) {
            udpPacketHandle((UDPPacket) p, tableData);
        } else if (p instanceof ARPPacket) {
            arpPacketHandle((ARPPacket) p, tableData);
        } else if (p instanceof ICMPPacket) {
            icmpPacketHandle((ICMPPacket)p, tableData);
        } else {
            IPPacket p1 = (IPPacket) p;
            tableData.setSrc(p1.src_ip.toString())
                    .setDest(p1.dst_ip.toString())
                    .setProtocol(Protocol.OTHER)
                    .setLength(p1.len);
            LogUtils.log("无法识别报文类型：", p);
        }
        tableData.setNum(NUM++);
        //时间处理逻辑
        if (firstPacketUSec == 0 && firstPacketSec == 0) {
            firstPacketUSec = p.usec;
            firstPacketSec = p.sec;
            tableData.setSec("0.000000");
        } else {
            long gap = (p.sec - firstPacketSec) * 1000000 + (p.usec - firstPacketUSec);
            String format = String.format("%.6f", gap * 0.000001);
            tableData.setSec(format);
        }
        return tableData;
    }

    public static String toBinString(byte[] header, byte[] data) {
        String[] h, d;
        if (header == null || header.length == 0) {
            h =  new String[0];
        } else {
            h =  HexUtil.byteArray2HexStringArray(header);
        }
        if (data == null || data.length == 0){
            d =  new String[0];
        } else  {
            d = HexUtil.byteArray2HexStringArray(data);
        }
        return toBinString(h, d);
    }

    public static String toBinString(String[] hexHeader, String[] hexData) {
        int headerLen = hexHeader.length;
        if (headerLen == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < headerLen; i++) {
            sb.append(hexHeader[i]).append(" ");
            if ((i + 1) % 8 == 0) {
                sb.append(" ");
            }
            if ((i + 1) % 16 == 0) {
                sb.append("\n");
            }
        }
        if (hexData != null && hexData.length != 0) {
            for (int i = 0; i < hexData.length; i++) {
                sb.append(hexData[i]).append(" ");
                if ((headerLen + i + 1) % 8 == 0) {
                    sb.append(" ");
                }
                if ((headerLen + i + 1) % 16 == 0) {
                    sb.append("\n");
                }
            }
        }
        return sb.toString();
    }

    private static void icmpPacketHandle(ICMPPacket p, TableData tableData) {
        tableData.setSrc(p.src_ip.toString())
                .setDest(p.dst_ip.toString())
                .setProtocol(Protocol.ICMP)
                .setLength(p.len);
    }

    private static void tcpPacketHandle(TCPPacket p, TableData tableData) {
        tableData.setSrc(p.src_ip.toString())
                .setDest(p.dst_ip.toString())
                .setProtocol(Protocol.TCP)
                .setLength(p.len);

    }
    private static void arpPacketHandle(ARPPacket p, TableData tableData) {
        tableData.setProtocol(Protocol.ARP);
        Object senderProtocolAddress = p.getSenderProtocolAddress();
        Object targetProtocolAddress = p.getTargetProtocolAddress();
        tableData.setSrc(senderProtocolAddress.toString());
        tableData.setDest(targetProtocolAddress.toString());
    }

    private static void udpPacketHandle(UDPPacket p, TableData tableData) {
        tableData.setSrc(p.src_ip.toString())
                .setDest(p.dst_ip.toString())
                .setProtocol(Protocol.UDP)
                .setLength(p.len);
    }

    //初始化，将数据还原，这两个数据，一个为了记录自增序号，一个为了记录耗时
    public static void init() {
        NUM = 1;
        firstPacketSec = 0;
        firstPacketUSec = 0;
        PacketQueue.clear();
    }
}
